package com.arges.dao;

import com.arges.util.GenericsUtils;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Table;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * Dao层公用类
 * 
 * @author arges
 * 
 * @param <T>
 * @param <PK>
 */
@SuppressWarnings("unchecked")
@Transactional
public class GenericDao<T, PK extends Serializable>
{
	private Class<T> entryClass;

	private Class<T> persistentClass;

	// @Autowired
	// private SessionFactory sessionFactory;

	@Autowired
	private EntityManagerFactory entityManagerFactory;

	public GenericDao()
	{
		this.entryClass = GenericsUtils.getSuperClassGenricType(getClass());
		ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
		this.persistentClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];
	}

	public GenericDao(Class<T> entryClass, EntityManagerFactory entityManagerFactory)
	{
		super();
		this.entryClass = entryClass;
		this.entityManagerFactory = entityManagerFactory;
	}

	/**
	 * <p>
	 * 通过id查询
	 * </p>
	 *
	 * @param id   id
	 * @param lock 状态
	 * @return @return {@link T }
	 * @author Arges
	 * @date 2022/04/15
	 */
	@SuppressWarnings("deprecation")
	public T findById(PK id, boolean lock)
	{
		T entity;
		if (lock)
		{
			entity = entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession().get(getPersistentClass(), id,
					LockMode.UPGRADE);
		}
		else
		{
			entity = entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession().get(getPersistentClass(),
					id);
		}
		if (entity == null)
		{
		}
		return entity;
	}

	/**
	 * <p>
	 * 获取
	 * </p>
	 *
	 * @return @return {@link Class }<{@link T }>
	 * @author Arges
	 * @date 2022/04/15
	 */
	public Class<T> getPersistentClass()
	{
		return persistentClass;
	}

	/**
	 * 打开session
	 * 
	 * @return
	 */
	public Session getSession()
	{
		return entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession();
	}

	/**
	 * 保存某个对象
	 *
	 * @param object
	 */
	public void save(T object)
	{
		getSession().save(object);
	}

	/**
	 * 保存某个对象 返回保存的主键
	 *
	 * @param object
	 */
	public Integer saveGetId(T object)
	{
		return (Integer) getSession().save(object);
	}

	/**
	 * 删除某个对象
	 * 
	 * @param object
	 */
	public void delete(T object)
	{
		getSession().delete(object);
	}

	/**
	 * 根据id删除某个对象
	 * 
	 * @param id
	 */
	public void delete(PK id)
	{
		if (null != get(id))
		{
			getSession().delete(get(id));
		}
	}

	/**
	 * 保存或者更新某个对象
	 * 
	 * @param object
	 */
	public void saveOrUpdate(Object object)
	{
		getSession().saveOrUpdate(object);
	}

	/**
	 * 更新某个对象
	 * 
	 * @param object
	 */
	public void update(T object)
	{
		getSession().update(object);
	}

	/**
	 * 刷新某个对象
	 * 
	 * @param object
	 */
	public void merge(T object)
	{
		getSession().merge(object);
	}

	/**
	 * 根据id查找某个对象
	 * 
	 * @param id 主键id
	 * @return T
	 */
	public T get(PK id)
	{
		return (T) getSession().get(entryClass, id);
	}

	/**
	 * 批量保存数据
	 * 
	 * @param collection 数据集合
	 */
	public void batchSave(Collection<? extends Object> collection)
	{
		Session session = getSession();
		if (null == collection)
		{
			return;
		}
		// 插入总数据条数
		int allNum = collection.size();
		// 每次插入25条记录最后剩余数据条数
		int restNum = allNum % 25;
		// 总共插入次数
		int flushNum = allNum / 25;
		Iterator<? extends Object> iterator = collection.iterator();
		// 记录插入的数据数
		int index = 0;
		while (iterator.hasNext())
		{
			Object object = iterator.next();
			session.save(object);
			++index;
			if (index <= flushNum * 25)
			{
				if (index % 25 == 0)
				{
					session.flush();
					session.clear();
				}
				else if (restNum > 0 && index == allNum)
				{
					session.flush();
					session.clear();
				}
			}
		}
	}

	/**
	 * 批量保存或者更新数据
	 * 
	 * @param collection 数据集合
	 */
	public void batchSaveOrUpdate(Collection<? extends Object> collection)
	{
		Session session = getSession();
		if (null == collection)
		{
			return;
		}
		// 插入总数据条数
		int allNum = collection.size();
		// 每次插入25条记录最后剩余数据条数
		int restNum = allNum % 25;
		// 总共插入次数
		int flushNum = allNum / 25;
		Iterator<? extends Object> iterator = collection.iterator();
		// 记录插入的数据数
		int index = 0;
		while (iterator.hasNext())
		{
			Object object = iterator.next();
			session.saveOrUpdate(object);
			++index;
			if (index <= flushNum * 25)
			{
				if (index % 25 == 0)
				{
					session.flush();
					session.clear();
				}
				else if (restNum > 0 && index == allNum)
				{
					session.flush();
					session.clear();
				}
			}
		}
	}

	/**
	 * 批量保存数据
	 * 
	 * @param collection 数据集合
	 */
	public void batchMerge(Collection<? extends Object> collection)
	{
		Session session = getSession();
		if (null == collection)
		{
			return;
		}
		// 插入总数据条数
		int allNum = collection.size();
		// 每次插入25条记录最后剩余数据条数
		int restNum = allNum % 256;
		// 总共插入次数
		int flushNum = allNum / 256;
		Iterator<? extends Object> iterator = collection.iterator();
		// 记录插入的数据数
		int index = 0;
		while (iterator.hasNext())
		{
			Object object = iterator.next();
			session.merge(object);
			++index;
			if (index <= flushNum * 256)
			{
				if (index % 256 == 0)
				{
					session.flush();
					session.clear();
				}
				else if (restNum > 0 && index == allNum)
				{
					session.flush();
					session.clear();
				}
			}
		}
	}

	/**
	 * 通过hql语句查询总条数
	 * 
	 * @param hql
	 * @param values 条件
	 * @return
	 */
	public long countQueryResult(String hql, Object... values)
	{
		hql = hql.substring(hql.toUpperCase().indexOf("FROM") + 4, hql.length());
		StringBuffer stringBuffer = new StringBuffer(" SELECT count(*) FROM (");
		stringBuffer.append(hql);
		stringBuffer.append(" )");
		Query query = getSession().createQuery(stringBuffer.toString());
		if (null != values && values.length > 0)
		{
			setQueryValue(query, values);
		}
		return (Long) query.uniqueResult();
	}

	/**
	 * 通过criteria语句查询总条数
	 * 
	 * @param clazz
	 * @param criterion 条件
	 * @return
	 */
	public long countCriteriaResult(Class<T> clazz, Criterion criterion)
	{
		Criteria criteria = getSession().createCriteria(clazz);
		if (null != criterion)
		{
			criteria.add(criterion);
		}

		return (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
	}

	/**
	 * 通过criteria语句查询总条数
	 * 
	 * @param criteria
	 * @return
	 */
	public long countCriteriaResult(Criteria criteria)
	{
		return (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
	}

	/**
	 * 设置查询条件参数
	 * 
	 * @param query
	 * @param values
	 */
	public void setQueryValue(Query query, Object... values)
	{
		for (int i = 0; i < values.length; i++)
		{
			query.setParameter(i, values[i]);
		}
	}

	/**
	 * 通过criteria语句查询总条数
	 *
	 */
	public long countCriteriaResult(Class<T> clazz, Criterion criterion, String searchValue, String property,
			String... entryName)
	{
		Criteria criteria = getSession().createCriteria(clazz);
		if (null != criterion)
		{
			criteria.add(criterion);
		}
		for (String string : entryName)
		{
			criteria.createCriteria(string).add(Restrictions.like(property, searchValue, MatchMode.ANYWHERE));

		}
		return (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
	}

	/**
	 * 根据id批量删除
	 * 
	 * @param ids
	 */
	public void batchDeleteByIds(String hql, Long[] ids)
	{
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(hql).append("(");
		for (int i = 0; i < ids.length; i++)
		{
			stringBuffer.append(ids[i] + ",");
		}
		stringBuffer.deleteCharAt(stringBuffer.length() - 1).append(")");
		getSession().createQuery(stringBuffer.toString()).executeUpdate();
	}

	/**
	 * 通过获取类上的@Table注解获取表名称
	 *
	 * @param clazz
	 * @return
	 */
	public static String getTableName(Class<?> clazz)
	{
		Table annotation = clazz.getAnnotation(Table.class);
		return annotation.name();
	}

	/**
	 * <p>
	 * 获取表名
	 * </p>
	 *
	 * @return @return {@link String }
	 * @author Arges
	 * @date 2022/04/15
	 */
	public String getTableName()
	{
		return getTableName(entryClass);
	}

	/**
	 * <p>
	 * 查询数据通过条件
	 * </p>
	 *
	 * @param criterion 标准
	 * @return @return {@link List }<{@link T }>
	 * @author Arges
	 * @date 2022/04/15
	 */
	public List<T> findDataByCondition(Criterion criterion)
	{
		Criteria criteria = getSession().createCriteria(entryClass);
		if (null != criterion)
		{
			criteria.addOrder(Order.desc("id"));
			criteria.add(criterion);
		}
		return criteria.list();
	}

	/**
	 * <p>
	 * 查询数据通过条件
	 * </p>
	 *
	 * @param criterion 标准
	 * @param order     排序
	 * @return @return {@link List }<{@link T }>
	 * @author Arges
	 * @date 2022/04/15
	 */
	public List<T> findDataByCondition(Criterion criterion, Order order)
	{
		Criteria criteria = getSession().createCriteria(entryClass);
		if (null != criterion)
		{
			criteria.addOrder(order);
			criteria.add(criterion);
		}
		return criteria.list();
	}

	/**
	 * 查找所有对象
	 */
	public List<T> findAll()
	{
		return getSession().createCriteria(entryClass).list();
	}

	/**
	 * <p>
	 * 查询单记录通过状态
	 * </p>
	 *
	 * @param condition 条件
	 * @param pojoClass pojo类
	 * @return @return {@link Object }
	 * @author Arges
	 * @date 2022/04/15
	 */
	public Object findSingleRecordByCondtion(Criterion condition, Class<?> pojoClass)
	{
		return getFirstRecordOrNull(findDataByCondition(condition));
	}

	/**
	 * 如果list内存在对象，则返回第一条记录，否则返回null
	 */
	protected Object getFirstRecordOrNull(List<?> pojoList)
	{
		Object result = null;
		if (pojoList.size() != 0)
		{
			result = pojoList.get(0);
		}
		return result;
	}

	/**
	 * <p>
	 * 查询通过条件
	 * </p>
	 *
	 * @param criterions 条件
	 * @param clazz      clazz
	 * @return @return {@link List }
	 * @author Arges
	 * @date 2022/04/15
	 */
	public List findByConditions(List<Criterion> criterions, Class<?> clazz)
	{
		Criteria criteria = getSession().createCriteria(clazz);
		for (Criterion criterion : criterions)
		{
			criteria.add(criterion);
		}
		return criteria.addOrder(Order.desc("id")).list();
	}

	/**
	 * 批量保存
	 * 
	 * @param collection
	 */
	@SuppressWarnings("rawtypes")
	public void saveAll(final Collection collection)
	{
		Session session = getSession();
		session.setFlushMode(FlushMode.AUTO);
		for (Object obj : collection)
		{
			session.save(obj);
		}
		session.flush();
	}

}
